import socket
import threading
from collections import defaultdict
tuple_space = defaultdict(str)
lock = threading.Lock()


def handle_client(client_socket, client_address):
    global tuple_space
    print(f"New client connection: {client_address}")
    while True:
        try:

            data = client_socket.recv(1024).decode('utf - 8')
            if not data:
                print(f"Client {client_address} disconnected")
                break
            size = int(data[:3])
            command = data[3]
            key = data[4:size].strip()
            value = data[size:].strip() if command == 'P' else ''
            response = ''

            with lock:
                if command == 'R':
                    if key in tuple_space:
                        response = f"OK ({key}, {tuple_space[key]}) read"
                        print(f"Client {client_address} executed READ operation, key: {key}, result: {response}")
                    else:
                        response = f"ERR {key} does not exist"
                        print(f"Client {client_address} executed READ operation, key: {key}, result: {response}")
                elif command == 'G':
                    if key in tuple_space:
                        response = f"OK ({key}, {tuple_space.pop(key)}) removed"
                        print(f"Client {client_address} executed GET operation, key: {key}, result: {response}")
                    else:
                        response = f"ERR {key} does not exist"
                        print(f"Client {client_address} executed GET operation, key: {key}, result: {response}")
                elif command == 'P':
                    if key in tuple_space:
                        response = f"ERR {key} already exists"
                        print(f"Client {client_address} executed PUT operation, key: {key}, result: {response}")
                    else:
                        tuple_space[key] = value
                        response = f"OK ({key}, {value}) added"
                        print(f"Client {client_address} executed PUT operation, key: {key}, value: {value}, result: {response}")

            # Send the response result to the client
            client_socket.send(response.encode('utf - 8'))
        except Exception as e:
            print(f"Error occurred while handling client {client_address}: {e}")
            break

    client_socket.close()


def start_server(port):

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(('0.0.0.0', port))
    server_socket.listen(10)
    print(f"Server is listening on port {port}...")

    while True:
        client_socket, client_address = server_socket.accept()
        client_thread = threading.Thread(target=handle_client, args=(client_socket, client_address))
        client_thread.start()


if __name__ == "__main__":
    SERVER_PORT = 51234
    start_server(SERVER_PORT)
